#include <pthread.h>
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <unistd.h>
using namespace std;

#define PTHREAD_NUM 5
int tickets = 10000;

class ThreadData
{
public:
    ThreadData(int& tickets, string& name, pthread_mutex_t& mutex)
        :_tickets(tickets)
        ,_name(name)
        ,_mutex(mutex)
    {}

    int total = 0;
    int& _tickets;              //临界资源
    string _name;
    pthread_mutex_t& _mutex;    //锁
};


template<class T>
class Thread
{

public:
    Thread(function<void(T*)> func, T* data)
        :_func(func)
        ,_data(data)
    {}

    static void* routine(void* args)
    {
        Thread* thread = reinterpret_cast<Thread*>(args);
        thread->_func(thread->_data);

        return nullptr;
    }

    void CreateRun()
    {
        int n = pthread_create(&_tid, nullptr, routine, this);
        if(n != 0) cout << "create: " << strerror(n) << endl;
    }

    void Join()
    {
        pthread_join(_tid, nullptr);
    }

    void detach()
    {
        pthread_detach(_tid);
    }

private:
    //希望能够得到临界资源
    T* _data;

    pthread_t _tid;
    function<void(T*)> _func;

};

// void func(ThreadData* td)
// {
//     while(true)
//     {
//         pthread_mutex_lock(&td->_mutex);
//         if(td->_tickets > 0)
//         {
//             cout << td->_name << ": " << tickets << endl;
//             td->_tickets--;  
//             pthread_mutex_unlock(&td->_mutex);

//             td->total++;
//         }
//         else 
//         {
//             pthread_mutex_unlock(&td->_mutex);
//             break;
//         }   
//     }
// }

class LockGuard
{
public:
    LockGuard(pthread_mutex_t* mutex)
        :_mutex(mutex)
    {
        pthread_mutex_lock(_mutex);
    }

    ~LockGuard()
    {
        pthread_mutex_unlock(_mutex);
    }

private:
    pthread_mutex_t* _mutex;

};

void func(ThreadData* td)
{
    while(true)
    {
        LockGuard lg(&td->_mutex);
        if(td->_tickets > 0)
        {
            cout << td->_name << ": " << tickets << endl;
            td->_tickets--;  

            td->total++;
        }
        else 
        {
            break;
        }   
    }
}

int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);

    vector<Thread<ThreadData>> vttd;
    vector<ThreadData*> del;
    for(int i=0; i<PTHREAD_NUM; i++)
    {
        string name = "pthread--"+to_string(i);
        ThreadData* thread = new ThreadData(tickets, name, mutex);

        vttd.emplace_back(func, thread);
        del.emplace_back(thread);
    }

    for(auto &e: vttd) e.CreateRun();
    for(auto &e: vttd) e.Join();

    for(auto &e: del) cout << e->_name << ": " <<e->total << endl;
    for(auto &e: del) delete e;

    return 0;
}

// #include <pthread.h>
// #include <iostream>
// #include <vector>
// #include <string>
// #include <cstdlib>
// #include <cstring>
// #include <functional>
// #include <unistd.h>
// using namespace std;

// #define PTHREAD_NUM 5
// int tickets = 10000;

// class ThreadData
// {
// public:
//     ThreadData(int& tickets, string& name, pthread_mutex_t& mutex)
//         :_tickets(tickets)
//         ,_name(name)
//         ,_mutex(mutex)
//     {}

//     int total = 0;
//     int& _tickets;              //临界资源
//     string _name;
//     pthread_mutex_t& _mutex;    //锁
// };


// template<class T>
// class Thread
// {

// public:
//     Thread(function<void(T)> func, T data)
//         :_func(func)
//         ,_data(data)
//     {}

//     static void* routine(void* args)
//     {
//         Thread* thread = reinterpret_cast<Thread*>(args);
//         thread->_func(thread->_data);

//         return nullptr;
//     }

//     void CreateRun()
//     {
//         int n = pthread_create(&_tid, nullptr, routine, this);
//         if(n != 0) cout << "create: " << strerror(n) << endl;
//     }

//     void Join()
//     {
//         pthread_join(_tid, nullptr);
//     }

//     void detach()
//     {
//         pthread_detach(_tid);
//     }

// private:
//     //希望能够得到临界资源
//     T _data;

//     pthread_t _tid;
//     function<void(T)> _func;

// };

// void func(ThreadData* td)
// {
//     while(true)
//     {
//         pthread_mutex_lock(&td->_mutex);
//         if(td->_tickets > 0)
//         {
//             cout << td->_name << ": " << tickets << endl;
//             td->_tickets--;  
//             pthread_mutex_unlock(&td->_mutex);

//             usleep(1000);
//             td->total++;
//         }
//         else 
//         {
//             pthread_mutex_unlock(&td->_mutex);
//             break;
//         }   
//     }
// }

// int main()
// {
//     pthread_mutex_t mutex;
//     pthread_mutex_init(&mutex, nullptr);

//     vector<Thread<ThreadData*>> vttd;
//     vector<ThreadData*> del;
//     for(int i=0; i<PTHREAD_NUM; i++)
//     {
//         string name = "pthread--"+to_string(i);
//         ThreadData* thread = new ThreadData(tickets, name, mutex);

//         vttd.emplace_back(func, thread);
//         del.emplace_back(thread);
//     }

//     for(auto &e: vttd) e.CreateRun();
//     for(auto &e: vttd) e.Join();
    
//     for(auto &e: del) cout << e->_name << ": " <<e->total << endl;
//     for(auto &e: del) delete e;

//     return 0;
// }